/**
 * ┏┓　　　┏┓
 * ┏┛┻━━━┛┻┓
 * ┃　　　　　　　┃
 * ┃　　　━　　　┃
 * ┃　┳┛　┗┳　┃
 * ┃　　　　　　　┃
 * ┃　　　┻　　　┃
 * ┃　　　　　　　┃
 * ┗━┓　　　┏━┛
 * 　　┃　　　┃神兽保佑
 * 　　┃　　　┃代码无BUG！
 * 　　┃　　　┗━━━┓
 * 　　┃　　　　　　　┣┓
 * 　    ┃　　　　　　　┏┛
 * 　　┗┓┓┏━┳┓┏┛
 * 　　　┃┫┫　┃┫┫
 * 　　　┗┻┛　┗┻┛
 **/
package com.asd.common.controller;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.asd.common.interceptors.bean.InvocationBean;
import com.asd.common.listener.reflex.AspectReflex;
import com.asd.common.listener.reflex.AutowiredReflex;
import com.asd.common.utils.MyUtil;
import com.asd.common.utils.PropKit;
import com.asd.common.utils.Render;
import com.asd.common.utils.RequestHelper;
import com.jfinal.kit.StrKit;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import java.io.*;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.Enumeration;
import java.util.Map;

/**
 * @author zhanqi
 * @date 2020年03月05日
 */
@SuppressWarnings({"unchecked"})
@Slf4j
public class BaseController extends HttpServlet {
    private static final long serialVersionUID = 1L;
    private static final int ERROR_CODE = -1;
    private static final int SUCCESS_CODE = 0;
    private HttpServletRequest req;
    private HttpServletResponse resp;

    @Override
    public void init() {
        // 执行必需的初始化
    }

    private void setPrint(HttpServletRequest req, Class<? extends BaseController> targetClass, Object methodName, String bodyStr) {
        if (PropKit.getBoolean("console", false)) {
            log.info("");
            log.info("-------------------------------------------------{}-------------------------------------------------", "jeasy");
            log.info("httpMethod:     {}", req.getMethod());
            log.info("scheme:         {}", req.getScheme());
            log.info("className:      {}", targetClass.getName());
            log.info("method:         {}", methodName);
            log.info("body:           {}", bodyStr);
            log.info("params:         {}", JSON.toJSONString(req.getParameterMap()));
            log.info("herders:        {}", JSON.toJSONString(RequestHelper.buildRequestHeaders(req)));
            log.info("------------------------------------------{}--------------------------------------", MyUtil.createtimeStr());
            log.info("");
        }
    }

    private void setConfig(HttpServletRequest req, HttpServletResponse resp) {
        setReq(req);
        setResp(resp);
        try {
            req.setCharacterEncoding("utf-8");
            req.setCharacterEncoding("utf-8");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        // resp.addHeader("Access-Control-Allow-Origin", "*");//跨域
        resp.addHeader("asd", "com.asd");
        resp.addHeader("author", "zhanqi email:339447532@qq.com");
        // 防止浏览器缓存
        resp.setHeader("Pragma", "No-cache");
        resp.setHeader("Cache-Control", "no-cache");
        resp.setDateHeader("Expires", 0);
    }

    private String getBody(HttpServletRequest request) {
        BufferedReader br;
        String str, body = "";
        try {
            br = request.getReader();
            while ((str = br.readLine()) != null) {
                body += str;
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return body;
    }

    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) {
        // 解决多线程下异常
        synchronized (BaseController.class) {
            setConfig(req, resp);
            String methodName = getReq().getAttribute("methodName").toString();
            methodName = StringUtils.isBlank(methodName) ? "index" : methodName;
            if (methodName.startsWith("/")) {
                methodName = methodName.substring(1);
            }
            // 扫描路由
            Class<? extends BaseController> targetClass = this.getClass();
            // @Autowired自动注入实现,接管class
            BaseController aop = new AutowiredReflex().autowired(targetClass, this);
            targetClass = aop.getClass();
            try {
                Method[] methods = targetClass.getDeclaredMethods();
                String finalMethodName = methodName;
                Method method = Arrays.asList(methods).stream().filter(e -> e.getName().equals(finalMethodName)).findFirst().get();
                if (method == null) {
                    Render.renderJson(getResp(), 404, "ERROR_404_找不到接口！");
                    return;
                }
                String bodyStr = getBody(req);
                InvocationBean inv = new InvocationBean();
                inv.setBodyStr(bodyStr);
                inv.setClazz(targetClass);
                inv.setMethod(method);
                inv.setReq(req);
                inv.setResp(resp);
                inv.setCt(aop);
                new AspectReflex().reflex(inv);
                // 调用找到的方法
                Object[] methodParameter = inv.getMethodParameters();
                Object obj;
                if (methodParameter != null) {
                    obj = method.invoke(aop, methodParameter);
                } else {
                    obj = method.invoke(aop);
                }
                Render.render(getResp(), obj);
                setPrint(req, targetClass, methodName, bodyStr);
            } catch (Exception e) {
                Render.renderJson(getResp(), 500, e.getMessage());
                return;
            }


        }

    }

    public HttpServletRequest getReq() {
        return req;
    }

    public void setReq(HttpServletRequest req) {
        this.req = req;
    }

    public HttpServletResponse getResp() {
        return resp;
    }

    public void setResp(HttpServletResponse resp) {
        this.resp = resp;
    }

    protected SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    // 获取字符串
    public String getParam(String param) {
        String paramValue = getReq().getParameter(param);
        if (StringUtils.isBlank(paramValue)) {
            return "";
        }
        return paramValue;
    }

    // 获取字符串
    public String getParam(String param, String default_) {
        String paramValue = getReq().getParameter(param);
        if (StringUtils.isBlank(paramValue)) {
            return default_;
        }
        return paramValue;
    }

    public Map<String, String[]> getParaMap() {
        return getReq().getParameterMap();
    }

    public Enumeration<String> getParaNames() {
        return getReq().getParameterNames();
    }

    public String[] getParaValues(String param) {
        return getReq().getParameterValues(param);
    }

    public Enumeration<String> getAttrNames() {
        return getReq().getAttributeNames();
    }

    public Integer[] getParaValuesToInt(String name) {
        String[] values = getReq().getParameterValues(name);
        if (values == null || values.length == 0) {
            return null;
        }
        Integer[] result = new Integer[values.length];
        for (int i = 0; i < result.length; i++) {
            result[i] = StrKit.isBlank(values[i]) ? null : Integer.parseInt(values[i]);
        }
        return result;
    }

    public Long[] getParaValuesToLong(String name) {
        String[] values = getReq().getParameterValues(name);
        if (values == null || values.length == 0) {
            return null;
        }
        Long[] result = new Long[values.length];
        for (int i = 0; i < result.length; i++) {
            result[i] = StrKit.isBlank(values[i]) ? null : Long.parseLong(values[i]);
        }
        return result;
    }

    // 获取数字
    public int getInt(String param) {
        int re = 0;
        String str = this.getParam(param);
        if (str.matches("\\d+")) {
            re = Integer.parseInt(str);
        }
        return re;
    }

    public Long getLong(String param) {
        long re = 0;
        String str = this.getParam(param);
        if (str.matches("\\d+")) {
            re = Long.parseLong(str);
        }
        return re;
    }

    public BigDecimal getBigDecimal(String param) {
        BigDecimal re = null;
        String str = this.getParam(param);
        re = new BigDecimal(str);
        return re;
    }

    // 填充请求参数到 Bean 对象
    public void getBean(Object bean) {
        Class<? extends Object> clazz = bean.getClass();

        Field[] fields = clazz.getDeclaredFields();
        if (null != fields && fields.length > 0) {

            try {
                for (Field field : fields) {
                    field.setAccessible(true);
                    String fname = field.getName();
                    Class<?> type = field.getType();

                    String paramv = this.getParam(fname);

                    if (type == String.class) {
                        field.set(bean, paramv);
                    } else if (type == Integer.class || type == int.class || type == Integer.TYPE) {
                        field.set(bean, this.getInt(fname));
                    } else if (type == Long.class || type == long.class || type == Long.TYPE) {
                        field.set(bean, this.getInt(fname));
                    } else if (type == BigDecimal.class) {
                        field.set(bean, this.getBigDecimal(fname));
                    } else if (type == Date.class) {
                        if (paramv.matches("\\d{4}[-]\\d{2}[-]\\d{2}[ ]\\d{2}[:]\\d{2}[:]\\d{2}")) {
                            field.set(bean, simpleDateFormat.parse(paramv));
                        }
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
                log.error("ERROR_002_填充对象值出错");
            }
        }
    }

    public <T> T getAttr(String name) {
        return (T) getReq().getAttribute(name);
    }

    public <T> T getAttr(String name, T defaultValue) {
        T result = (T) getReq().getAttribute(name);
        return result != null ? result : defaultValue;
    }

    public void setAttr(String key, Object value) {
        getReq().setAttribute(key, value);
    }

    public String getCookie(String name, String defaultValue) {
        Cookie cookie = getCookieObject(name);
        return cookie != null ? cookie.getValue() : defaultValue;
    }

    public String getCookie(String name) {
        return getCookie(name, null);
    }

    public Integer getCookieToInt(String name) {
        String result = getCookie(name);
        return result != null ? Integer.parseInt(result) : null;
    }

    public Integer getCookieToInt(String name, Integer defaultValue) {
        String result = getCookie(name);
        return result != null ? Integer.parseInt(result) : defaultValue;
    }

    public Long getCookieToLong(String name) {
        String result = getCookie(name);
        return result != null ? Long.parseLong(result) : null;
    }

    public Long getCookieToLong(String name, Long defaultValue) {
        String result = getCookie(name);
        return result != null ? Long.parseLong(result) : defaultValue;
    }

    public Cookie getCookieObject(String name) {
        Cookie[] cookies = getReq().getCookies();
        if (cookies != null)
            for (Cookie cookie : cookies)
                if (cookie.getName().equals(name))
                    return cookie;
        return null;
    }

    public Cookie[] getCookieObjects() {
        Cookie[] result = getReq().getCookies();
        return result != null ? result : new Cookie[0];
    }

    public BaseController setCookie(String name, String value, int maxAgeInSeconds, boolean isHttpOnly) {
        return doSetCookie(name, value, maxAgeInSeconds, null, null, isHttpOnly);
    }

    public BaseController setCookie(String name, String value, int maxAgeInSeconds) {
        return doSetCookie(name, value, maxAgeInSeconds, null, null, null);
    }

    public BaseController setCookie(Cookie cookie) {
        getResp().addCookie(cookie);
        return this;
    }

    public BaseController setCookie(String name, String value, int maxAgeInSeconds, String path, boolean isHttpOnly) {
        return doSetCookie(name, value, maxAgeInSeconds, path, null, isHttpOnly);
    }

    public BaseController setCookie(String name, String value, int maxAgeInSeconds, String path) {
        return doSetCookie(name, value, maxAgeInSeconds, path, null, null);
    }

    public BaseController setCookie(String name, String value, int maxAgeInSeconds, String path, String domain,
                                    boolean isHttpOnly) {
        return doSetCookie(name, value, maxAgeInSeconds, path, domain, isHttpOnly);
    }

    public BaseController removeCookie(String name) {
        return doSetCookie(name, null, 0, null, null, null);
    }

    public BaseController removeCookie(String name, String path) {
        return doSetCookie(name, null, 0, path, null, null);
    }

    public BaseController removeCookie(String name, String path, String domain) {
        return doSetCookie(name, null, 0, path, domain, null);
    }

    private BaseController doSetCookie(String name, String value, int maxAgeInSeconds, String path, String domain,
                                       Boolean isHttpOnly) {
        Cookie cookie = new Cookie(name, value);
        cookie.setMaxAge(maxAgeInSeconds);
        if (path == null) {
            path = "/";
        }
        cookie.setPath(path);

        if (domain != null) {
            cookie.setDomain(domain);
        }
        if (isHttpOnly != null) {
            cookie.setHttpOnly(isHttpOnly);
        }
        getResp().addCookie(cookie);
        return this;
    }

    /**
     * PrintWriter向前端打印数据
     *
     * @param code
     * @param msg
     * @param data
     */
    private void outPrint(int code, Object msg, Object data) {
        getResp().setContentType("text/html;charset=utf-8");
        try (PrintWriter out = getResp().getWriter()) {
            JSONObject json = new JSONObject();
            json.put("code", code);
            json.put("msg", msg);
            json.put("data", data);
            out.print(JSON.toJSONString(json));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void render(String path) {
        try {
            getReq().getRequestDispatcher(PropKit.get("baseViewPath") + path).forward(getReq(), getResp());
        } catch (ServletException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void redirect(String path) {
        try {
            getResp().sendRedirect(path);
        } catch (IOException e) {

            e.printStackTrace();
        }
    }

    // 直接输出值
    public void renderText(String msg) {
        getResp().setContentType("text/html;charset=utf-8");
        try (PrintWriter out = getResp().getWriter()) {
            out.print(msg);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    // 输出 json
    public void renderJson(Object object) {
        getResp().setContentType("text/html;charset=utf-8");
        try (PrintWriter out = getResp().getWriter()) {
            out.print(JSON.toJSONString(object));
            out.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void renderFail(Object msg) {
        outPrint(ERROR_CODE, msg, new JSONObject());
    }

    public void renderFail(Object msg, Object data) {
        outPrint(ERROR_CODE, msg, data);
    }

    public void renderFail(int code, Object msg, Object data) {
        outPrint(code, msg, data);

    }

    public void renderSuccess(Object msg) {
        outPrint(SUCCESS_CODE, msg, new JSONObject());

    }

    public void renderSuccess(Object msg, Object data) {
        outPrint(SUCCESS_CODE, msg, data == null ? new JSONObject() : data);

    }

    // 从 session 中获取值

    public Object getSessionAttr(String param) {
        return getReq().getSession().getAttribute(param);
    }

    // 设置 session 值

    public void setSessionAttr(String key, Object val) {
        getReq().getSession().setAttribute(key, val);
    }

    // 删除 session 中的值

    public void removeSessionAttr(String key) {
        getReq().getSession().removeAttribute(key);
    }

    // 销毁 session

    public void invalidateSession() {
        getReq().getSession().invalidate();
    }

    public String basePath() {
        return getReq().getScheme() + "://" + getReq().getServerName() + ":" + getReq().getServerPort()
                + getReq().getContextPath() + "/";
    }
    /**
     * xml转对象
     *
     * @param xml
     * @return
     * @throws JAXBException
     */
    public <T> T  convertToObj(String xml,Class<T> clazz) throws JAXBException {
        StringReader reader = new StringReader(xml);
        JAXBContext jaxbContext = JAXBContext.newInstance(clazz);
        Unmarshaller jaxbUnmarshaller = jaxbContext.createUnmarshaller();
        T obj = (T)jaxbUnmarshaller.unmarshal(reader);
        return obj;
    }

    /**
     * 将对象转为流程XML
     *
     * @param obj
     * @return
     * @throws JAXBException
     */
    public String convertToXML(Object obj) throws JAXBException {
        JAXBContext jaxbContext = JAXBContext.newInstance(obj.getClass());
        StringWriter writer = new StringWriter();
        Marshaller marshaller = jaxbContext.createMarshaller();
        marshaller.marshal(obj, writer);
        String xmlStr = writer.toString();
        xmlStr = StringUtils.replace(xmlStr, "&quot;", "'");
        return xmlStr;
    }
}
